$KCODE = 'u'

require 'logger'
require 'kconv'
require 'yaml'

require 'rubygems'
require 'hpricot'
require 'ostruct'

module Rire
  VERSION = '0.0.1'

  class PropertyBase < Array
    @@instances = {}

    def self.fetch(text)
      return {}  unless @@instances.key?(self)
      define = @@instances[self]

      if define.key?(:foreach)
        src = text
        src = define[:setup].call(src)   if define.key?(:setup)

        records = self.new

        src.each do |s|
          define[:foreach].call(s)

          struct = {}
          if define.key?(:has_property)
            define[:has_property].each do |o|
              struct[o[:name]] = o[:options]
            end
          end
          records << OpenStruct.new(struct)
        end

        records
      else
        if define[:options].key?(:xpath)
          records = Hpricot(text).search(define[:options][:xpath]).collect {|r| r.to_s}
        elsif define[:options].key?(:regexp)
          records = text.scan(%r{#{define[:options][:regexp]}}m).collect {|r| r.to_s}
        end

        entries = self.new

        define[:record].call

        records.each do |r|
          struct = {}
          define[:has_property].each do |o|
            struct[o[:name]] = get_property(r, o[:options])
          end
          entries << OpenStruct.new(struct)
		end

        entries.map! {|entry|
          r = entry
          r = define[:after_hook].call(entry)   if define.key?(:after_hook)
          r
        }.compact
      end
    end

    def self.setup(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:setup] = block
    end

    def self.foreach(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:foreach] = block
    end

    def self.has_property(name, options)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:has_property] = [] unless @@instances[self].key?(:has_property)
      @@instances[self][:has_property] << {:name => name, :options => options}
    end

    def self.record(options, &block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:options] = options
      @@instances[self][:record] = block
    end

    def self.after_hook(&block)
      @@instances[self] = {}  unless @@instances.key?(self)
      @@instances[self][:after_hook] = block
    end

    # private methods
    def self.get_property(text, options)
      if options.key?(:xpath)
        doc = Hpricot(text)

        elem = doc.at(options[:xpath])
        case elem
        when Hpricot::Text
          elem.to_s
        when Hpricot::Elem
          case options[:expr]
          when /inner_text/
            elem.inner_text
          when /text/
            elem.search('/').map{|e| e if e.class == Hpricot::Text }.compact.join('')
          when /@(.+)/
            elem[$1]
          else
            elem.inner_text
          end
        else
          ''
        end
      elsif options.key?(:regexp)
        text.match(%r{#{options[:regexp]}}m).to_a[1]
      end
    end
    private_class_method :get_property

  end

end

